package khoihd;

import jade.core.Agent;
import jade.core.AID;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

import java.util.Vector;
import java.util.Date;

public class BookBuyerAgent extends Agent {
	private Vector sellerAgents = new Vector();
	
//	private BookBuyerGui myGui;
	
	protected void setup() {
		
		
		System.out.println("Buyer-agent " + getAID().getName() + " is ready.");
		
		//Update the list of seller agents every minute
		addBehaviour(new TickerBehaviour(this, 60000) {
			
			@Override
			protected void onTick() {
				// TODO Auto-generated method stub
				DFAgentDescription template = new DFAgentDescription();
				ServiceDescription sd = new ServiceDescription();
				sd.setType("Book-selling");
				template.addServices(sd);
				try {
					DFAgentDescription[] result = DFService.search(myAgent, template);
					sellerAgents.clear();
					for (int i=0; i<result.length; i++)
						sellerAgents.addElement(result[i]);
				}
				catch (FIPAException fe) {
					fe.printStackTrace();
				}
			}
		});
		
		//get names of seller agents as arguments
//		Object[] args = getArguments();
//		if (args != null) {
//			for (int i=0; i<args.length; i++) {
//				AID seller = new AID((String) args[i], AID.ISLOCALNAME);
//				sellerAgents.add(seller);
//			}
//		}
		
		//show the GUI
//		myGui = new BookBuyerGuiImpl();
//		myGui.setAgent(this);
//		myGui.show();
	}
	
	protected void takeDown() {
//		if (myGui != null) {
//			myGui.dispose();
//		}
		
		System.out.println("Buyer-agent " + getAID().getName() + " terminated.");
	}
	
	public void purchase(String title, int maxPrice, Date deadline) {
		addBehaviour(new PurchaseManager(this, title, maxPrice, deadline));
	}
	
	private class PurchaseManager extends TickerBehaviour {

		private String title;
		private int maxPrice;
		private long deadline, initTime, deltaT;
		
		private PurchaseManager(Agent a, String t, int mp, Date d) {
			super(a, 6000);
			title = t;
			maxPrice = mp;
			deadline = d.getTime();
			initTime = System.currentTimeMillis();
			deltaT = deadline - initTime;
		}
		
		protected void onTick() {
			// TODO Auto-generated method stub
			long currentTime = System.currentTimeMillis();
			if (currentTime > deadline) {
				stop();
			}
			else {
				//compute the currently acceptable price and start a negotiation
				long elapsedTime = currentTime - initTime;
				int acceptablePrice = (int) (maxPrice * (elapsedTime / deltaT));
				myAgent.addBehaviour(new BookNegotiator(title, acceptablePrice, this));
			}
		}
	}
	
	private class BookNegotiator extends Behaviour {
		private String title;
		private int maxPrice;
		private PurchaseManager manager;
		private AID bestSeller;
		private int bestPrice;
		private int repliesCnt = 0;
		private MessageTemplate mt;
		private int step = 0;
		
		public BookNegotiator(String t, int p, PurchaseManager m) {
			super(null);
			title = t;
			maxPrice = p;
			manager = m;
		}
		
		public void action() {
			// TODO Auto-generated method stub
			switch (step) {
			case 0:
				//Send the cfp to all sellers
				ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
				for (int i=0; i< sellerAgents.size(); i++) {
					cfp.addReceiver((AID)sellerAgents.elementAt(i));
				}
				cfp.setContent(title);
				cfp.setConversationId("book-trade");
				cfp.setReplyWith("cfp" + System.currentTimeMillis());
				myAgent.send(cfp);
				
				//Prepare the template to get proposals
				mt = MessageTemplate.and(MessageTemplate.MatchConversationId("book-trade"), MessageTemplate.MatchInReplyTo(cfp.getReplyWith()));
				step = 1;
				break;
			case 1:
				//receive all proposals/refusals from seller agents
				ACLMessage reply = myAgent.receive(mt);
				if (reply != null) {
					//Reply received
					if (reply.getPerformative() == ACLMessage.PROPOSE) {
						//This is an offer
						int price = Integer.parseInt(reply.getContent());
						if (bestSeller == null || price < bestPrice) {
							//this is the best offer at present
							bestPrice = price;
							bestSeller = reply.getSender();
						}
					}
					repliesCnt++;
					if (repliesCnt >= sellerAgents.size())
						//received all replies
						step = 2;
				}
				else {
					block();
				}
				break;
			case 2:
				if (bestSeller != null && bestPrice <= maxPrice) {
					//send the purchase order to the seller that provided the best offer
					ACLMessage order = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
					order.addReceiver(bestSeller);
					order.setContent(title);
					order.setConversationId("book-trade");
					order.setReplyWith("order"+System.currentTimeMillis());
					myAgent.send(order);
					//Prepare the template to get the purchase order reply
					mt = MessageTemplate.and(MessageTemplate.MatchConversationId("book-trade"), MessageTemplate.MatchInReplyTo(order.getReplyWith()));
					step = 3;
				}
				else {
					//receive no acceptable proposals, terminate
					step = 4;
				}
				break;
			case 3:
				//receive order reply
				reply = myAgent.receive(mt);
				if (reply != null) {
					//purchase order reply received
					if (reply.getPerformative() == ACLMessage.INFORM) {
						manager.stop();
					}
					step = 4;
				}
				else {
					block();
				}
				break;
			}			
		}

		public boolean done() {
			// TODO Auto-generated method stub
			return step == 4;
		}
		
	}
}
